from langflow import CustomComponent
from langchain.docstore.document import Document
import requests
from typing import Optional
from langflow.utils.util import save_remote_file
import docx
import time, json
from loguru import logger
from docx.oxml.table import CT_Tbl
from docx.oxml.text.paragraph import CT_P
from docx.table import _Cell, Table
from docx.text.paragraph import Paragraph


class GetWord(CustomComponent):
    display_name: str = "Word文件输入"
    description: str = "用于在技能执行时接收用户的Word文件输入， 调试编译时可进行预填。"

    def build_config(self):
        return {
            "word_url": {
                "display_name": "word文件路径",
                "info": "The URL to make the request to",
                "required": False,
                "value": "请输入文件路径",
                "advanced":True
            },
             "word_file_desc": {
                "display_name": "输入内容说明",
                "required": True,
                "value": "",
                "multiline": True,
            },
            "local_word_file_path":{"field_type":"file","suffixes":[".docx"],"file_types":["docx"],"required":False,"file_path":"test.docx","display_name": "Word文件（可预填调试）"},
            "code": {"show": False},
        }

    def parse_a_docx_table(self,some_doc_table):
        res_list = list()
        for i in range(len(some_doc_table.rows)):
            cur_row = some_doc_table.row_cells(i)
            cur_row_list = [x.text.replace('\n', '') for x in cur_row]
            res_list.append(cur_row_list)

        if len(res_list) > 2:
            markdown_str = "| " + " | ".join(res_list[0]) + " |\n"
            markdown_str += "| " + " | ".join("---" for j in range(len(res_list[0]))) + " |\n"
            for res in res_list[1:]:
                markdown_str += "| " + " | ".join(cell for cell in res) + " |\n"

        elif len(res_list) == 2:
            if res_list[1] and len(set(res_list[1])) > 1 and res_list[1][0]:
                markdown_str = "| " + " | ".join(res_list[0]) + " |\n"
                markdown_str += "| " + " | ".join("---" for j in range(len(res_list[0]))) + " |\n"
                for res in res_list[1:]:
                    markdown_str += "| " + " | ".join(cell for cell in res) + " |\n"
            else:
                markdown_str = "| " + " | ".join(res_list[0]) + " |\n"
        else:
            markdown_str = "| " + " | ".join(res_list[0]) + " |\n"
        return markdown_str


    # 将docx中的paragraphs和tables元素按块的方式读取
    def docx_iter_block_items(self,parent):
        """
        Yield each paragraph and table child within *parent*, in document order.
        Each returned value is an instance of either Table or Paragraph. *parent*
        would most commonly be a reference to a main Document object, but
        also works for a _Cell object, which itself can contain paragraphs and tables.
        """
        from docx.document import Document
        if isinstance(parent, Document):
            parent_elm = parent.element.body
        elif isinstance(parent, _Cell):
            parent_elm = parent._tc
        else:
            raise ValueError("something's not right")

        for child in parent_elm.iterchildren():
            if isinstance(child, CT_P):
                yield Paragraph(child, parent)
            elif isinstance(child, CT_Tbl):
                yield Table(child, parent)

    # 将一个docx文件按顺序解析为列表对象，元素为字符串和markdown
    def get_doc_content(self,some_fpath):
        doc = docx.Document(some_fpath)
        ele_list = self.docx_iter_block_items(doc)
        ele_result_list = []
        for ele in ele_list:
            try:
                table_result = ele.text
            except:
                table_result = self.parse_a_docx_table(ele)
            if table_result:
                ele_result_list.append(table_result)
        return ele_result_list
    
    def get_document(
            self, word_url: str, local_word_file_path: str
    ) -> Document:
        url = word_url if word_url.endswith('.docx') else local_word_file_path
        if url.startswith('http') and  url.endswith('.docx'):
            try:
                response = requests.get(url)
            except Exception as exc:
                raise exc
            try:
                _, file_name = url.rsplit('/', 1)
                unique_str = file_name.rsplit('.', 1)[0] + "_" + str(int(time.time()))
                file_path = save_remote_file(file_name, unique_str)
                with open(file_path, 'wb') as f:
                    f.write(response.content)
                try:
                    ele_result_list = self.get_doc_content(file_path)
                    return Document(page_content=json.dumps({"result": ele_result_list}, ensure_ascii=False))
                except Exception as exc:
                    raise exc
                
            except Exception as exc:
                raise exc
                
        elif not url.startswith('http') and url.endswith('.docx'):
            ele_result_list = self.get_doc_content(url)
            return Document(page_content=json.dumps(ele_result_list, ensure_ascii= False))

        else:
            logger.error(f"file_path is error :Please check the input, start with http and end with .doc or .docx")
            raise ValueError(f"file_path is error :Please check the input, start with http and end with .docx")

    def build(
            self,
            word_url: str,
            word_file_desc: str,
            local_word_file_path: str,
    ) -> Document:
        document = self.get_document(word_url,local_word_file_path)
        self.repr_value = document
        return document